001 /*
002 * Copyright 2004-2005 Stephen McConnell
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.transit.tools;
020
021 import java.io.IOException;
022 import java.io.InputStream;
023
024 import java.net.URI;
025 import java.util.Map;
026 import java.util.Hashtable;
027
028 import java.util.List;
029 import java.util.ArrayList;
030
031 import net.dpml.util.ElementHelper;
032
033 import net.dpml.lang.Part;
034 import net.dpml.lang.Resource;
035
036 import org.apache.tools.ant.BuildException;
037 import org.apache.tools.ant.BuildListener;
038 import org.apache.tools.ant.Project;
039 import org.apache.tools.ant.ComponentHelper;
040
041 import org.w3c.dom.Element;
042
043 /**
044 * The plugin task handles the establishment of ant tasks, listeners, and antlibs derived
045 * from a classloader established by the transit sub-system.
046 *
047 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
048 * @version 1.0.1
049 */
050 public class PluginTask extends TransitTask
051 {
052 private Map m_map = new Hashtable();
053
054 /**
055 * The uri of the plugin to load.
056 */
057 private String m_uri;
058
059 /**
060 * Overloaded plugin urn.
061 */
062 private String m_urn;
063
064 /**
065 * A list of tasks declared by the plugin declaration.
066 */
067 private List m_tasks = new ArrayList();
068
069 /**
070 * List of listeners declared by the plugin task declaration.
071 */
072 private List m_listeners = new ArrayList();
073
074 /**
075 * List of antlibs declared by the plugin task declaration.
076 */
077 private List m_antlibs = new ArrayList();
078
079 /**
080 * Plugin name delalred by the plugin task declaration.
081 */
082 private String m_name;
083
084 /**
085 * A flag indicating that nested directives have been provided.
086 */
087 private boolean m_flag = false;
088
089 /**
090 * Set the project.
091 * @param project the current project
092 */
093 public void setProject( Project project )
094 {
095 setTaskName( "plugin" );
096 super.setProject( project );
097 }
098
099 /**
100 * Create and associate a new antlib urn entry with the plugin.
101 * @return the new antlib entry
102 */
103 public Antlib createAntlib()
104 {
105 m_flag = true;
106 final Antlib antlib = new Antlib();
107 m_antlibs.add( antlib );
108 return antlib;
109 }
110
111 /**
112 * Create and associate a new task entry with the plugin.
113 * @return the new task entry
114 */
115 public Task createTask()
116 {
117 m_flag = true;
118 final Task task = new Task();
119 m_tasks.add( task );
120 return task;
121 }
122
123 /**
124 * Create and associate a new build listener with the plugin.
125 * @return the new listener entry
126 */
127 public Listener createListener()
128 {
129 m_flag = true;
130 final Listener listener = new Listener();
131 m_listeners.add( listener );
132 return listener;
133 }
134
135 /**
136 * Set the artifact uri of the plugin from which the task is to be loaded.
137 * @param uri an artifact plugin uri
138 */
139 public void setUri( String uri )
140 {
141 m_uri = uri;
142 }
143
144 /**
145 * Overload the urn to assign to the plugin.
146 * @param urn the urn to use
147 */
148 public void setUrn( String urn )
149 {
150 m_urn = urn;
151 }
152
153 /**
154 * Return the artifact uri of the plugin.
155 * @return the plugin uri
156 */
157 public URI getUri()
158 {
159 try
160 {
161 return new URI( m_uri );
162 }
163 catch( Throwable e )
164 {
165 final String error =
166 "Cound not convert the supplied uri spec ["
167 + m_uri
168 + "] to a formal URI.";
169 throw new BuildException( error, e, getLocation() );
170 }
171 }
172
173 /**
174 * Load the plugin and handle registration of listeners, tasks, and antlib
175 * declarations based on the nested nested task directives.
176 * @exception BuildException if an error occurs during plugin loading or deployment
177 */
178 public void execute() throws BuildException
179 {
180 if( null == m_uri )
181 {
182 final String error =
183 "Missing uri attribute.";
184 throw new BuildException( error );
185 }
186
187 final Project project = getProject();
188 final ComponentHelper helper =
189 ComponentHelper.getComponentHelper( project );
190
191 if( !m_flag )
192 {
193 createAntlib();
194 }
195
196 try
197 {
198 URI uri = new URI( m_uri );
199 Part part = getPart( uri );
200
201 ClassLoader loader = part.getClassLoader();
202 Task[] tasks = (Task[]) m_tasks.toArray( new Task[0] );
203 if( tasks.length > 0 )
204 {
205 for( int i=0; i < tasks.length; i++ )
206 {
207 try
208 {
209 Task task = tasks[i];
210 String classname = task.getClassname();
211 String name = task.getName();
212 Class c = loader.loadClass( classname );
213 helper.addTaskDefinition( name, c );
214 }
215 catch( Throwable e )
216 {
217 final String error =
218 "Failed to load a named task ["
219 + tasks[i].getName()
220 + "] from the plugin ["
221 + uri
222 + "].";
223 throw new BuildException( error, e, getLocation() );
224 }
225 }
226 }
227
228 Listener[] listeners = (Listener[]) m_listeners.toArray( new Listener[0] );
229 for( int i=0; i < listeners.length; i++ )
230 {
231 Listener listener = listeners[i];
232 String classname = listener.getClassname();
233 Class c = loader.loadClass( classname );
234 Object object = c.newInstance();
235 if( object instanceof BuildListener )
236 {
237 BuildListener instance = (BuildListener) object;
238 getProject().addBuildListener( instance );
239 log( "registered listener: " + instance.getClass().getName() );
240 }
241 else
242 {
243 final String error =
244 "The plugin ["
245 + uri
246 + "] establishing the class ["
247 + object.getClass().getName()
248 + "] could not be registered as a project listener because it does not implement the ["
249 + BuildListener.class.getName()
250 + "] interface.";
251 throw new BuildException( error, getLocation() );
252 }
253 }
254
255 Antlib[] antlibs = (Antlib[]) m_antlibs.toArray( new Antlib[0] );
256 for( int i=0; i < antlibs.length; i++ )
257 {
258 loadAntlib( uri, loader, helper, antlibs[i] );
259 }
260 }
261 catch( BuildException e )
262 {
263 throw e;
264 }
265 catch( Throwable e )
266 {
267 final String error = "Unable to load the plugin ["
268 + m_uri
269 + "] due to "
270 + e.toString();
271 throw new BuildException( error, e, getLocation() );
272 }
273 }
274
275 private Part getPart( URI uri ) throws IOException
276 {
277 return Part.load( uri, true );
278 }
279
280 /**
281 * Load an antlib.
282 * @param classloader the classloader from which the antlib will be loaded
283 * @param helper the component helper
284 * @param antlib the antlib to load
285 * @exception Exception if it doesn't work out
286 */
287 private void loadAntlib(
288 URI uri, ClassLoader classloader, ComponentHelper helper, Antlib antlib ) throws Exception
289 {
290 Part part = getPart( uri );
291
292 String resource = antlib.getPath();
293 if( null == resource )
294 {
295 if( part instanceof Resource )
296 {
297 Resource res = (Resource) part;
298 resource = res.getPath();
299 }
300 }
301 if( null == resource )
302 {
303 final String error =
304 "Resource path for the antlib is not declared in the plugin descriptor "
305 + "or antlib directive ["
306 + uri
307 + "]";
308 throw new BuildException( error, getLocation() );
309 }
310
311 String urn = getAntLibURN( antlib, part );
312 if( null == urn )
313 {
314 final String error =
315 "URN for the antlib is not declared in the plugin descriptor "
316 + "or antlib directive ["
317 + uri
318 + "]";
319 throw new BuildException( error, getLocation() );
320 }
321
322 InputStream input = classloader.getResourceAsStream( resource );
323 Element root = ElementHelper.getRootElement( input );
324 Element[] tasks = ElementHelper.getChildren( root, "taskdef" );
325 for( int i=0; i < tasks.length; i++ )
326 {
327 Element task = tasks[i];
328 String name = ElementHelper.getAttribute( task, "name" );
329 String classname = ElementHelper.getAttribute( task, "classname" );
330 loadTaskDef( classloader, helper, classname, urn + ":" + name );
331 }
332
333 Element[] types = ElementHelper.getChildren( root, "typedef" );
334 for( int i=0; i < types.length; i++ )
335 {
336 Element type = types[i];
337 String name = ElementHelper.getAttribute( type, "name" );
338 String classname = ElementHelper.getAttribute( type, "classname" );
339 loadTypeDef( classloader, helper, classname, urn + ":" + name );
340 }
341 }
342
343 private String getAntLibURN( Antlib antlib, Part part )
344 {
345 if( null != m_urn )
346 {
347 return m_urn;
348 }
349 String urn = antlib.getURN();
350 if( null != urn )
351 {
352 return urn;
353 }
354 else
355 {
356 if( part instanceof Resource )
357 {
358 Resource res = (Resource) part;
359 return res.getURN();
360 }
361 else
362 {
363 return null;
364 }
365 }
366 }
367
368 /**
369 * Load a single task defintion.
370 * @param loader the classloader from which the task will be loaded
371 * @param helper the component helper
372 * @param classname the task classname
373 * @param name the task name
374 * @exception BuildException if an error occurs while attempting to load the task
375 */
376 private void loadTaskDef( ClassLoader loader, ComponentHelper helper, String classname, String name )
377 throws BuildException
378 {
379 if( getProject().getTaskDefinitions().get( name ) != null )
380 {
381 return;
382 }
383
384 try
385 {
386 Class c = loader.loadClass( classname );
387 helper.addTaskDefinition( name, c );
388 log( "installed taskdef: " + name, Project.MSG_VERBOSE );
389 }
390 catch( BuildException e )
391 {
392 throw e;
393 }
394 catch( Throwable e )
395 {
396 final String error =
397 "Unable to load task ["
398 + name
399 + "] from class ["
400 + classname
401 + "].";
402 throw new BuildException( error, e, getLocation() );
403 }
404 }
405
406 /**
407 * Load a single type defintion.
408 * @param loader the classloader from which the type will be loaded
409 * @param helper the component helper
410 * @param classname the type classname
411 * @param name the task type
412 * @exception BuildException if an error occurs while attempting to load the task
413 */
414 private void loadTypeDef( ClassLoader loader, ComponentHelper helper, String classname, String name )
415 throws BuildException
416 {
417 if( getProject().getDataTypeDefinitions().get( name ) != null )
418 {
419 return;
420 }
421
422 try
423 {
424 Class c = loader.loadClass( classname );
425 helper.addDataTypeDefinition( name, c );
426 log( "installed typedef: " + name, Project.MSG_VERBOSE );
427 }
428 catch( BuildException e )
429 {
430 throw e;
431 }
432 catch( Throwable e )
433 {
434 final String error =
435 "Unable to load type ["
436 + name + "] from class ["
437 + classname
438 + "].";
439 throw new BuildException( error, e, getLocation() );
440 }
441 }
442
443 /**
444 * Nested element with the <plugin> element declaring the name and class of
445 * a task to be loaded from the classloader established by the transit plugin descriptor.
446 */
447 public static class Task
448 {
449 /**
450 * The task name.
451 */
452 private String m_name;
453
454 /**
455 * The task classname.
456 */
457 private String m_classname;
458
459 /**
460 * Set the task name.
461 * @param name the name of the task
462 */
463 public void setName( final String name )
464 {
465 m_name = name;
466 }
467
468 /**
469 * Set the task classname.
470 * @param classname the task classname
471 */
472 public void setClass( final String classname )
473 {
474 m_classname = classname;
475 }
476
477 /**
478 * Return the task classname.
479 * @return the classname
480 * @exception BuildException if the class attribute is missing
481 */
482 public String getClassname() throws BuildException
483 {
484 if( null == m_classname )
485 {
486 final String error =
487 "Missing class attribute.";
488 throw new BuildException( error );
489 }
490 return m_classname;
491 }
492
493 /**
494 * Return the task name.
495 * @return the name
496 * @exception BuildException if the name attribute is missing
497 */
498 public String getName() throws BuildException
499 {
500 if( null == m_name )
501 {
502 final String error =
503 "Missing name attribute.";
504 throw new BuildException( error );
505 }
506 return m_name;
507 }
508 }
509
510 /**
511 * Nested element with the <plugin> element declaring the name and class of
512 * a project listener to be loaded from the classloader established by the transit plugin descriptor.
513 */
514 public static class Listener
515 {
516 /**
517 * The listener classname.
518 */
519 private String m_classname;
520
521 /**
522 * Set the task classname.
523 * @param classname the task classname
524 */
525 public void setClass( final String classname )
526 {
527 m_classname = classname;
528 }
529
530 /**
531 * Return the task classname.
532 * @return the classname
533 * @exception BuildException if the class attribute is missing
534 */
535 public String getClassname() throws BuildException
536 {
537 if( null == m_classname )
538 {
539 final String error =
540 "Missing class attribute.";
541 throw new BuildException( error );
542 }
543 return m_classname;
544 }
545 }
546
547 /**
548 * Nested element with the <plugin> element declaring a packaged resource and urn of
549 * an antlib descriptor to be loaded from the classloader established by the transit plugin
550 * descriptor.
551 */
552 public static class Antlib
553 {
554 /**
555 * The antlib urn.
556 */
557 private String m_urn;
558
559 /**
560 * The antlib descriptor resource path.
561 */
562 private String m_path;
563
564 /**
565 * Set the urn for this antlib.
566 * @param urn the antlib urn
567 */
568 public void setUrn( final String urn )
569 {
570 m_urn = urn;
571 }
572
573 /**
574 * Return the antlib urn.
575 * @return the urn (possibly null in which case the a urn must be declared
576 * within the plugin descriptor)0
577 */
578 public String getURN()
579 {
580 return m_urn;
581 }
582
583 /**
584 * Set the antlib resource path
585 * @param path the resource path
586 */
587 public void setResource( final String path )
588 {
589 m_path = path;
590 }
591
592 /**
593 * Return the antlib resource path.
594 * @return the path (possibly null in which case the resource reference
595 * must exist in the plugin descriptor)
596 */
597 public String getPath()
598 {
599 return m_path;
600 }
601 }
602 }